package org.xshop.service.impl;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.transaction.annotation.Transactional;
import org.xshop.BizConstants.TradeStyle;
import org.xshop.bean.Order;
import org.xshop.bean.OrderPay;
import org.xshop.bean.OrderWater;
import org.xshop.mapper.TOrderMapper;
import org.xshop.model.TGoods;
import org.xshop.model.TGoodsAttribute;
import org.xshop.model.TGoodsLimit;
import org.xshop.model.TGoodsPromotion;
import org.xshop.model.TMember;
import org.xshop.model.TMemberAddress;
import org.xshop.model.TOrder;
import org.xshop.model.TOrderAddress;
import org.xshop.model.TOrderFee;
import org.xshop.model.TOrderOther;
import org.xshop.model.TOrderSku;
import org.xshop.model.TOrderStateLog;
import org.xshop.model.TShoppingCart;
import org.xshop.service.ITOrderService;
import org.xshop.service.ITOrderStateService;

import com.alibaba.fastjson.JSON;
import com.weibo.api.motan.config.springsupport.annotation.MotanService;

import top.ibase4j.core.Constants;
import top.ibase4j.core.base.BaseServiceImpl;
import top.ibase4j.core.support.Pagination;
import top.ibase4j.core.support.generator.Sequence;
import top.ibase4j.core.support.pay.WxPay;
import top.ibase4j.core.support.pay.WxPayment;
import top.ibase4j.core.util.AlipayUtil;
import top.ibase4j.core.util.CacheUtil;
import top.ibase4j.core.util.DataUtil;
import top.ibase4j.core.util.DateUtil;
import top.ibase4j.core.util.DateUtil.DATE_PATTERN;
import top.ibase4j.core.util.InstanceUtil;
import top.ibase4j.core.util.PropertiesUtil;
import top.ibase4j.core.util.WeChatUtil;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author ShenHuaJie
 * @since 2017-04-21
 */
@MotanService(interfaceClass = ITOrderService.class)
@CacheConfig(cacheNames = "order")
public class OrderService extends BaseServiceImpl<TOrder, TOrderMapper> implements ITOrderService {
    @Autowired
    private MemberService memberService;
    @Autowired
    private MemberAddressService memberAddressService;
    @Autowired
    private GoodsService goodsService;
    @Autowired
    private OrderAddressService orderAddressService;
    @Autowired
    private OrderFeeService feeService;
    @Autowired
    private OrderSkuService orderSkuService;
    @Autowired
    private OrderOtherService otherService;
    @Autowired
    private OrderStateLogService stateLogService;
    @Autowired
    private ShoppingCartService shoppingCartService;
    @Autowired
    private ITOrderStateService orderStateService;
    @Autowired
    private SysService sysService;

    @Override
    public Pagination<TOrder> query(Map<String, Object> params) {
        Pagination<TOrder> page = super.query(params);
        for (TOrder order : page.getRecords()) {
            order.setMember(memberService.getInfo(order.getMemberId()));
            TOrderFee fee = new TOrderFee();
            fee.setOrderId(order.getId());
            order.setFee(feeService.selectOne(fee));

            TOrderSku orderSku = new TOrderSku();
            orderSku.setOrderId(order.getId());
            List<TOrderSku> skus = orderSkuService.queryList(orderSku);
            List<Object> skuList = InstanceUtil.newArrayList();
            BigDecimal quantity = new BigDecimal(0);
            for (TOrderSku sku : skus) {
                TGoods goods = JSON.parseObject(sku.getSkuInfo(), TGoods.class);
                if (goods != null) {
                    goods.setStock(sku.getQuantity());
                    quantity = quantity.add(sku.getQuantity());
                    skuList.add(goods);
                }
            }
            order.setSkuList(skuList);
            order.setQuantity(quantity);
        }
        return page;
    }

    @Override
    public TOrder queryById(Long id) {
        TOrder order = super.queryById(id);

        if (order != null) {
            order = orderStateService.updateOrderState(order);

            order.setMember(memberService.getInfo(order.getMemberId()));

            TOrderAddress address = new TOrderAddress();
            address.setOrderId(order.getId());
            order.setAddress(orderAddressService.selectOne(address));

            TOrderFee fee = new TOrderFee();
            fee.setOrderId(order.getId());
            order.setFee(feeService.selectOne(fee));

            TOrderSku orderSku = new TOrderSku();
            orderSku.setOrderId(order.getId());
            List<TOrderSku> skus = orderSkuService.queryList(orderSku);
            List<Object> skuList = InstanceUtil.newArrayList();
            for (TOrderSku sku : skus) {
                TGoods goods = JSON.parseObject(sku.getSkuInfo(), TGoods.class);
                if (goods != null) {
                    goods.setStock(sku.getQuantity());
                    skuList.add(goods);
                }
            }
            order.setSkuList(skuList);

            TOrderOther other = new TOrderOther();
            other.setOrderId(order.getId());
            order.setOther(otherService.selectOne(other));

            TOrderStateLog stateLog = new TOrderStateLog();
            stateLog.setOrderId(order.getId());
            order.setStateLogs(stateLogService.queryList(stateLog));
        }

        return order;

    }

    @Override
    @Transactional
    public TOrder update(TOrder record) {
        record.setOrderTime(new Date());
        if (record.getOrderNo() == null) {
            String orderNo = "T" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + record.getMemberId()
            + RandomUtils.nextInt(100, 999);
            record.setOrderNo(orderNo);
        }
        TOrder order = super.update(record);

        if (record.getAddressId() != null) {

        } else if (record.getAddress() != null) {
            record.getAddress().setOrderId(order.getId());
            orderAddressService.update(record.getAddress());
        }
        if (record.getFee() != null) {
            record.getFee().setOrderId(order.getId());
            feeService.update(record.getFee());
        }
        if (record.getOther() != null) {
            record.getOther().setOrderId(order.getId());
            otherService.update(record.getOther());
        }
        if (!record.getState().equals(order.getState())) {
            stateLogService.updateLog(InstanceUtil.to(order, TOrderStateLog.class));
        }
        return order;
    }

    @Override
    @Transactional
    public TOrder updateOrderInfo(TOrder record) {
        TOrder order = super.update(record);
        if (!record.getState().equals(order.getState())) {
            stateLogService.updateLog(InstanceUtil.to(order, TOrderStateLog.class));
        }
        return order;
    }

    /**
     * TODO 下单
     */
    @Override
    @Transactional
    public TOrder create(Order record, Boolean... extraArgs) {
        boolean fromCart = extraArgs.length > 0 && extraArgs[0];

        if (record.getSkuList() != null) {
            switch (record.getSkuType()) {
            case "1":
                String[] skus = record.getSkuList().split(",");
                Map<Long, List<String[]>> map = InstanceUtil.newLinkedHashMap();
                for (String sku : skus) {
                    String[] info = sku.split("\\|");
                    TGoods goods = goodsService.queryById(Long.valueOf(info[0]));
                    if (map.get(goods.getStoreId()) == null) {
                        map.put(goods.getStoreId(), InstanceUtil.newArrayList());
                    }
                    map.get(goods.getStoreId()).add(info);
                }
                for (List<String[]> infos : map.values()) {
                    TOrder order = InstanceUtil.to(record, TOrder.class);
                    order.setMemberId(record.getMemberId());
                    order.setSkuType(record.getSkuType());
                    order.setOrderTime(new Date());
                    String orderNo = "T" + DateUtil.format(new Date(), "yyyyMMddHHmmss") + record.getMemberId()
                    + RandomUtils.nextInt(100, 999);
                    order.setOrderNo(orderNo);
                    order = super.update(order);
                    order.setDispatcherFee(record.getDispatcherFee());
                    if (record.getAddressId() != null) {
                        TMemberAddress memberAddress = memberAddressService.queryById(record.getAddressId());
                        TOrderAddress orderAddress = InstanceUtil.to(memberAddress, TOrderAddress.class);
                        orderAddress.setId(null);
                        orderAddress.setOrderId(order.getId());
                        order.setAddress(orderAddress);
                        orderAddressService.update(order.getAddress());
                    }
                    for (String[] info : infos) {
                        saveOrderGoods(order, info, fromCart);
                    }
                    stateLogService.updateLog(InstanceUtil.to(order, TOrderStateLog.class));
                    return order;
                }
                break;
            default:
                throw new RuntimeException("错误的商品类型.");
            }
        }
        return null;
    }

    @Override
    @Transactional
    public Long deleteOrder(Long id) {
        TOrder order = super.queryById(id);
        if (order == null) {
            return 2L;
        }
        if (Integer.valueOf(order.getState()) >= 3) {
            return 3L;
        }

        Map<String, Object> columnMap = InstanceUtil.newHashMap("orderId", order.getId());
        orderAddressService.deleteByEntity(InstanceUtil.transMap2Bean(columnMap, TOrderAddress.class));
        orderSkuService.deleteByEntity(InstanceUtil.transMap2Bean(columnMap, TOrderSku.class));
        feeService.deleteByEntity(InstanceUtil.transMap2Bean(columnMap, TOrderFee.class));
        otherService.deleteByEntity(InstanceUtil.transMap2Bean(columnMap, TOrderOther.class));
        TOrderStateLog log = new TOrderStateLog();
        log.setOrderId(order.getId());
        log.setState(-1);
        stateLogService.updateLog(log);
        if ("1".equals(order.getPayType())) {
            deleteWxOrder(order);
        }
        super.delete(id);
        return 1L;
    }

    // TODO 支付回调
    @Override
    @Transactional
    public TOrder pay(OrderPay record) {
        TOrder order = new TOrder();
        order.setOrderNo(record.getOrderNo());
        order = selectOne(order);

        if (order == null) {
            throw new RuntimeException("订单号错误.");
        }
        if (record.getPayTime() == null) {
            order.setPayTime(new Date());
        } else {
            order.setPayTime(record.getPayTime());
        }
        order.setPayType(record.getPayType());
        order.setPayOrderId(record.getPayOrderId());
        order.setPayResult(record.getPayResult());

        order = orderStateService.updatePayResult(order, true);
        stateLogService.updateLog(InstanceUtil.to(order, TOrderStateLog.class));
        if ("1".equals(order.getPayType())) {
            deleteWxOrder(order);
        }
        return queryById(order.getId());
    }

    /**
     * TODO 下单
     */
    @Override
    @Transactional
    public Object createPayOrder(Long orderId, String payType, String ip) {
        TOrder order = new TOrder();
        order.setId(orderId);
        order = selectOne(order);

        if (order == null) {
            throw new RuntimeException("订单号错误.");
        }
        if (Integer.valueOf(order.getState()) > 1) {
            throw new RuntimeException("订单状态错误.");
        }
        TOrderFee fee = new TOrderFee();
        fee.setOrderId(order.getId());
        order.setFee(feeService.selectOne(fee));
        if (TradeStyle.WECHAT.toString().equals(order.getPayType()) && StringUtils.isNotBlank(order.getPayOrderId())) {
            Map<String, String> sign = WxPayment.buildOrderPaySign(PropertiesUtil.getString("wx.appId"),
                PropertiesUtil.getString("wx.mch_id"), order.getPayOrderId(), "APP",
                PropertiesUtil.getString("wx.partnerKey"));
            sign.put("amount", order.getFee().getPayAmount().toString());
            return sign;
        }

        TOrderSku orderSku = new TOrderSku();
        orderSku.setOrderId(order.getId());
        List<TOrderSku> skus = orderSkuService.queryList(orderSku);
        StringBuilder info = new StringBuilder();
        for (TOrderSku sku : skus) {
            TGoods goods = JSON.parseObject(sku.getSkuInfo(), TGoods.class);
            info = info.append(goods.getGoodsName()).append(order.getFee().getPayAmount()).append("元");
        }
        order.setPayType(payType);
        TMember member = memberService.queryById(order.getMemberId());
        if (TradeStyle.WECHAT.toString().equals(order.getPayType())) { // 微信
            String callBack = sysService.getSysParam("WX-PAY-CALLBACK");
            Map<String, String> sign = WeChatUtil.pushOrder(order.getOrderNo(), info.toString(), member.getPhone(),
                order.getFee().getPayAmount(), null, ip, callBack, null);
            order.setPayOrderId(sign.get("prepayid"));
            super.update(order);
            sign.put("amount", order.getFee().getPayAmount().toString());
            sign.put("out_trade_no", order.getOrderNo());
            return sign;
        } else if (TradeStyle.WXAPPLET.toString().equals(order.getPayType())) { // 微信小程序
            String callBack = sysService.getSysParam("WX-PAY-CALLBACK");
            Map<String, String> sign = WeChatUtil.pushOrder(PropertiesUtil.getString("wx.applet.mch_id"),
                PropertiesUtil.getString("wx.applet.appId"), PropertiesUtil.getString("wx.applet.partnerKey"), "JSAPI",
                order.getOrderNo(), info.toString(), member.getPhone(), order.getFee().getPayAmount(), null, ip,
                callBack, member.getWxOpenId());
            order.setPayOrderId(sign.get("prepayid"));
            super.update(order);
            sign.put("out_trade_no", order.getOrderNo());
            return sign;
        } else if (TradeStyle.ALIPAY.toString().equals(order.getPayType())) { // 支付宝
            String callBack = sysService.getSysParam("ALI-PAY-CALLBACK");
            String sign = AlipayUtil.getSign(order.getOrderNo(), info.toString(), member.getPhone(),
                order.getFee().getPayAmount(), ip, "5m", callBack);
            super.update(order);
            return sign;
        }
        return null;
    }

    /**
     * 保存订单商品信息
     */
    @Transactional
    private void saveOrderGoods(TOrder order, String[] info, boolean fromCart) {
        String cacheKey = Constants.CACHE_NAMESPACE + "ODR-GD-" + info[0];
        String requestId = Sequence.next().toString();
        if (CacheUtil.getLock(cacheKey, requestId, 60 * 60 * 24)) {
            try {
                TGoods goods = goodsService.queryById(Long.valueOf(info[0]));
                if (goods != null) {
                    BigDecimal quantity = new BigDecimal(info[1]);
                    if (goods.getIsSale() != 1) {
                        throw new RuntimeException("商品【" + goods.getGoodsName() + "】未上架.");
                    }
                    if (goods.getStock().compareTo(quantity) == -1) {
                        throw new RuntimeException("商品【" + goods.getGoodsName() + "】库存不足.");
                    }
                    if (goods.getIsLimit() == 1) {
                        TGoodsLimit limit = goods.getLimit();
                        Date now = DateUtil.stringToDate(DateUtil.getDate());
                        if (limit != null && !limit.getStartTime().after(now) && !limit.getEndTime().before(now)) {
                            if (limit.getLimitNumber().compareTo(quantity) == -1) {
                                throw new RuntimeException("商品【" + goods.getGoodsName() + "】购买数量超过限购.");
                            }
                        }
                    }
                    order.setStoreId(goods.getStoreId());
                    updateOrderInfo(order);
                    TOrderSku orderSku = new TOrderSku();
                    orderSku.setQuantity(quantity);
                    orderSku.setOrderId(order.getId());
                    orderSku.setSkuInfo(JSON.toJSONString(goods));
                    if (goods.getTypeIdIi() != null) {
                        orderSku.setSkuClassify(goods.getTypeIdIi().toString());
                    }
                    orderSku.setSkuId(goods.getId());
                    orderSkuService.update(orderSku);
                    // 订单来自购物车，则清除购物车中的同一商品
                    if (fromCart) {
                        TShoppingCart shoppingCart = new TShoppingCart();
                        shoppingCart.setMemberId(order.getMemberId());
                        shoppingCart.setSkuId(goods.getId());
                        shoppingCart.setSkuAttribute(info[2]);
                        shoppingCart = shoppingCartService.selectOne(shoppingCart);
                        if (shoppingCart != null) {
                            shoppingCartService.delete(shoppingCart.getId());
                        }
                    }

                    TOrderFee fee = new TOrderFee();
                    fee.setOrderId(order.getId());
                    TOrderFee orderFee = feeService.selectOne(fee);
                    if (orderFee != null) {
                        fee = orderFee;
                    }
                    BigDecimal goodsMoney = DataUtil.ifNull(fee.getGoodsMoney(), new BigDecimal("0"));
                    fee.setGoodsMoney(goods.getSalePrice());
                    if (goods.getIsPromotion() == 1) {
                        TGoodsPromotion promotion = goods.getPromotion();
                        Date now = DateUtil.stringToDate(DateUtil.getDate());
                        if (!promotion.getStartTime().after(now) && !promotion.getEndTime().before(now)) {
                            fee.setGoodsMoney(promotion.getPrice());
                        }
                    }
                    String[] attrs = info[2].split("\\-");
                    for (String attr : attrs) {
                        for (TGoodsAttribute attribute : goods.getAttributes()) {
                            if (attr.equals(attribute.getAttributeValue())) {
                                fee.setGoodsMoney(fee.getGoodsMoney().subtract(attribute.getEffectPrice()));
                            }
                        }
                    }
                    fee.setPayAmount(fee.getGoodsMoney().multiply(quantity)
                        .add(DataUtil.ifNull(fee.getPayAmount(), new BigDecimal("0"))));
                    if (fee.getDeliveryCosts() == null || fee.getDeliveryCosts().intValue() == 0) {
                        fee.setPayAmount(
                            fee.getPayAmount().add(DataUtil.ifNull(order.getDispatcherFee(), new BigDecimal("0"))));
                        fee.setDeliveryCosts(order.getDispatcherFee());
                    }
                    fee.setGoodsMoney(fee.getGoodsMoney().multiply(quantity).add(goodsMoney));
                    feeService.update(fee);
                } else {
                    throw new RuntimeException("商品不存在.");
                }
            } finally {
                CacheUtil.unLock(cacheKey, requestId);
            }
        } else {
            saveOrderGoods(order, info, fromCart);
        }
    }

    /**
     * 删除微信订单
     */
    @Transactional
    private void deleteWxOrder(TOrder order) {
        try {
            Map<String, String> params = new HashMap<String, String>();
            params.put("appid", PropertiesUtil.getString("wx.appId"));
            params.put("mch_id", PropertiesUtil.getString("wx.mch_id"));
            params.put("out_trade_no", order.getOrderNo());
            params = WxPayment.buildSignAfterParasMap(params, PropertiesUtil.getString("wx.partnerKey"));
            String result = WxPay.closeOrder(params);
            Map<String, String> resultMap = WxPayment.xmlToMap(result);
            logger.info(resultMap);
        } catch (Exception e) {
            logger.error("删除微信订单异常", e);
        }
    }

    @Override
    public Object queryAll(Map<String, Object> param) {
        param.put("enable", 1);
        int pageNum = param.get("pageNum") == null ? 1 : Integer.parseInt(param.get("pageNum").toString());
        int pageSize = 10;
        param.put("offset", (pageNum - 1) * pageSize);
        param.put("limit", pageSize);
        String expiryTime = DateUtil.format(DateUtil.addDate(new Date(), Calendar.DAY_OF_MONTH, -1),
            DATE_PATTERN.YYYYMMDDHHMMSS);
        param.put("expiryTime", expiryTime);
        Pagination<TOrder> page = query(param);
        List<OrderWater> list = InstanceUtil.newArrayList();
        if (page != null && page.getRecords().size() != 0) {
            for (TOrder order : page.getRecords()) {
                List<Object> goods = order.getSkuList();
                if (goods == null) {
                    continue;
                }
                OrderWater orderWater = new OrderWater();
                List<TGoods> goodsList = InstanceUtil.newArrayList();
                for (int i = 0; i < goods.size(); i++) {
                    TGoods good = (TGoods)goods.get(i);
                    orderWater.setStore(good.getStore());
                    good.setStore(null);
                    goodsList.add(good);
                }
                orderWater.setGoods(goodsList);
                order.setSkuList(null);
                order.setMember(null);
                orderWater.setOrder(order);
                list.add(orderWater);
            }

        }
        return list;
    }

    @Override
    @Transactional
    public Object updateFromDelete(Map<String, Object> param) {
        Long memberId = Long.parseLong(param.get("memberId").toString());
        String ids = param.get("ids").toString();
        String idArray[] = ids.split(",");
        for (String idStr : idArray) {
            Long id = Long.parseLong(idStr);
            TOrder order = new TOrder();
            order.setId(id);
            order = super.queryById(id);
            if (order == null || !memberId.equals(order.getMemberId())) {
                continue;
            }
            order.setEnable(0);
            order.setState("2");
            updateOrderInfo(order);
        }
        return idArray.length;
    }

    @Override
    public Integer countNewOrder(Integer second) {
        Map<String, Object> param = InstanceUtil.newHashMap();
        param.put("startPayTime", DateUtil.addDate(new Date(), Calendar.SECOND, -second));
        return mapper.countNewOrder(param);
    }
}
